package nape.dynamics;
import zpp_nape.Config;
import zpp_nape.Const;
import zpp_nape.util.Flags;
import DummyNapeMain;
import zpp_nape.util.Math;
import zpp_nape.util.Names;
import zpp_nape.util.Pool;
import zpp_nape.util.Lists;
import zpp_nape.shape.Circle;
import zpp_nape.shape.Edge;
import zpp_nape.space.Space;
import zpp_nape.shape.Polygon;
import zpp_nape.shape.Shape;
import zpp_nape.phys.FeatureMix;
import zpp_nape.phys.FluidProperties;
import zpp_nape.phys.Material;
import zpp_nape.phys.Body;
import zpp_nape.geom.AABB;
import zpp_nape.geom.Distance;
import zpp_nape.geom.Mat23;
import zpp_nape.geom.Vec2;
import zpp_nape.geom.VecMath;
import zpp_nape.geom.Collide;
import zpp_nape.dynamics.Arbiter;
import zpp_nape.dynamics.Contact;
import zpp_nape.dynamics.InteractionFilter;
import zpp_nape.constraint.Constraint;
import zpp_nape.dynamics.SpaceArbiterList;
import zpp_nape.constraint.Matrices;
import zpp_nape.constraint.PivotJoint;
import zpp_nape.constraint.WeldJoint;
import nape.util.Flags;
import nape.util.Lists;
import nape.util.Debug;
import nape.space.Space;
import nape.shape.Circle;
import nape.shape.Polygon;
import nape.shape.Edge;
import nape.shape.Shape;
import nape.phys.FluidProperties;
import nape.phys.Material;
import nape.phys.Body;
import nape.geom.AABB;
import nape.geom.Geom;
import nape.geom.Mat23;
import nape.geom.Vec2;
import nape.dynamics.Contact;
import nape.dynamics.InteractionFilter;
#if swc@:keep#end class ArbiterType{
    public function new(){
        if(!ZPP_Flags.internal)throw "Error: Cannot instantiate "+"ArbiterType"+" derp!";
    }
    @:keep inline public function toString(){
        if(false)return "";
        
        else if(this==COLLISION)return"COLLISION";
        else if(this==FLUID)return"FLUID";
        else if(this==SENSOR)return"SENSOR";
        else return "";
    }
    public static var COLLISION(flibget_COLLISION,never):ArbiterType;
    inline static function flibget_COLLISION(){
        if(ZPP_Flags.ArbiterType_COLLISION==null){
            ZPP_Flags.internal=true;
            ZPP_Flags.ArbiterType_COLLISION=new ArbiterType();
            ZPP_Flags.internal=false;
        }
        return ZPP_Flags.ArbiterType_COLLISION;
    }
    public static var FLUID(flibget_FLUID,never):ArbiterType;
    inline static function flibget_FLUID(){
        if(ZPP_Flags.ArbiterType_FLUID==null){
            ZPP_Flags.internal=true;
            ZPP_Flags.ArbiterType_FLUID=new ArbiterType();
            ZPP_Flags.internal=false;
        }
        return ZPP_Flags.ArbiterType_FLUID;
    }
    public static var SENSOR(flibget_SENSOR,never):ArbiterType;
    inline static function flibget_SENSOR(){
        if(ZPP_Flags.ArbiterType_SENSOR==null){
            ZPP_Flags.internal=true;
            ZPP_Flags.ArbiterType_SENSOR=new ArbiterType();
            ZPP_Flags.internal=false;
        }
        return ZPP_Flags.ArbiterType_SENSOR;
    }
}
#if swc@:keep#end class ArbiterList{
    public var zpp_inner:ZPP_ArbiterList;
    public var length(flibget_length,never):Int;
    #if false inline function flibget_length(){
        zpp_inner.valmod();
        if(zpp_inner.zip_length){
            zpp_inner.zip_length=false;
            if(true){
                zpp_inner.user_length=0;
                {
                    var cx_ite=zpp_inner.inner.begin();
                    while(cx_ite!=null){
                        var i=cx_ite.elem();
                        if(i.active)zpp_inner.user_length++;
                        cx_ite=cx_ite.next;
                    }
                };
            }
            else zpp_inner.user_length=zpp_inner.length;
        }
        return zpp_inner.user_length;
    }
    #else inline function flibget_length(){
        return zpp_gl();
    }
    public function zpp_gl(){
        zpp_inner.valmod();
        if(zpp_inner.zip_length){
            zpp_inner.zip_length=false;
            if(true){
                zpp_inner.user_length=0;
                {
                    var cx_ite=zpp_inner.inner.begin();
                    while(cx_ite!=null){
                        var i=cx_ite.elem();
                        if(i.active)zpp_inner.user_length++;
                        cx_ite=cx_ite.next;
                    }
                };
            }
            else zpp_inner.user_length=zpp_inner.length;
        }
        return zpp_inner.user_length;
    }
    public function zpp_vm(){
        zpp_inner.valmod();
    }
    #end#if swc@:keep#end public static function fromArray(array:#if flash9 Array<Dynamic>#else Array<Arbiter>#end){
        var ret=new ArbiterList();
        for(i in array){
            #if flash9 if(!Std.is(i,Arbiter))throw "Error: Array contains non "+"Arbiter"+" types.";
            #end ret.push(i);
        }
        return ret;
    }
    #if flash9#if swc@:keep#end public static function fromVector(vector:flash.Vector<Arbiter>){
        var ret=new ArbiterList();
        for(i in vector)ret.push(i);
        return ret;
    }
    #end#if swc@:keep#end public function at(index:Int):Arbiter{
        #if false zpp_inner.valmod();
        #else zpp_vm();
        #end if(index<0||index>=length)throw "Error: Index out of bounds";
        if(zpp_inner.reverse_flag)index=(length-1-index);
        if(!true){
            if(index<zpp_inner.at_index||zpp_inner.at_ite==null){
                zpp_inner.at_index=index;
                zpp_inner.at_ite=zpp_inner.inner.iterator_at(index);
            }
            else{
                while(zpp_inner.at_index!=index){
                    zpp_inner.at_index++;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
        }
        else{
            if(index<zpp_inner.at_index||zpp_inner.at_ite==null){
                zpp_inner.at_index=0;
                zpp_inner.at_ite=zpp_inner.inner.begin();
                while(true){
                    var x=zpp_inner.at_ite.elem();
                    if(x.active)break;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
            while(zpp_inner.at_index!=index){
                zpp_inner.at_index++;
                zpp_inner.at_ite=zpp_inner.at_ite.next;
                while(true){
                    var x=zpp_inner.at_ite.elem();
                    if(x.active)break;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
        }
        return zpp_inner.at_ite.elem().wrapper();
    }
    #if swc@:keep#end public function push(obj:Arbiter){
        if(zpp_inner.immutable)throw "Error: "+"Arbiter"+"List is immutable";
        zpp_inner.modify_test();
        #if false zpp_inner.valmod();
        #else zpp_vm();
        #end var cont=if(zpp_inner.adder!=null)zpp_inner.adder(obj)else true;
        if(cont){
            if(zpp_inner.reverse_flag)zpp_inner.inner.add(obj.zpp_inner);
            else{
                if(zpp_inner.push_ite==null)zpp_inner.push_ite=empty()?null:zpp_inner.inner.iterator_at(length-1);
                zpp_inner.push_ite=zpp_inner.inner.insert(zpp_inner.push_ite,obj.zpp_inner);
            }
            zpp_inner.invalidate();
            if(zpp_inner.post_adder!=null)zpp_inner.post_adder(obj);
        }
        return cont;
    }
    #if swc@:keep#end public function unshift(obj:Arbiter){
        if(zpp_inner.immutable)throw "Error: "+"Arbiter"+"List is immutable";
        zpp_inner.modify_test();
        #if false zpp_inner.valmod();
        #else zpp_vm();
        #end var cont=if(zpp_inner.adder!=null)zpp_inner.adder(obj)else true;
        if(cont){
            if(zpp_inner.reverse_flag){
                if(zpp_inner.push_ite==null)zpp_inner.push_ite=empty()?null:zpp_inner.inner.iterator_at(length-1);
                zpp_inner.push_ite=zpp_inner.inner.insert(zpp_inner.push_ite,obj.zpp_inner);
            }
            else zpp_inner.inner.add(obj.zpp_inner);
            zpp_inner.invalidate();
            if(zpp_inner.post_adder!=null)zpp_inner.post_adder(obj);
        }
        return cont;
    }
    #if swc@:keep#end public function pop():Arbiter{
        if(zpp_inner.immutable)throw "Error: "+"Arbiter"+"List is immutable";
        zpp_inner.modify_test();
        if(empty())throw "Error: Cannot remove from empty list";
        #if false zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=null;
        if(zpp_inner.reverse_flag){
            ret=zpp_inner.inner.front();
            var retx=ret.wrapper();
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.pop();
        }
        else{
            if(zpp_inner.at_ite!=null&&zpp_inner.at_ite.next==null)zpp_inner.at_ite=null;
            var ite=length==1?null:zpp_inner.inner.iterator_at(length-2);
            ret=ite==null?zpp_inner.inner.front():ite.next.elem();
            var retx=ret.wrapper();
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.erase(ite);
        }
        zpp_inner.invalidate();
        var retx=ret.wrapper();
        return retx;
    }
    #if swc@:keep#end public function shift():Arbiter{
        if(zpp_inner.immutable)throw "Error: "+"Arbiter"+"List is immutable";
        zpp_inner.modify_test();
        if(empty())throw "Error: Cannot remove from empty list";
        #if false zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=null;
        if(zpp_inner.reverse_flag){
            if(zpp_inner.at_ite!=null&&zpp_inner.at_ite.next==null)zpp_inner.at_ite=null;
            var ite=length==1?null:zpp_inner.inner.iterator_at(length-2);
            ret=ite==null?zpp_inner.inner.front():ite.next.elem();
            var retx=ret.wrapper();
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.erase(ite);
        }
        else{
            ret=zpp_inner.inner.front();
            var retx=ret.wrapper();
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.pop();
        }
        zpp_inner.invalidate();
        var retx=ret.wrapper();
        return retx;
    }
    #if swc@:keep#end public function add(obj:Arbiter)return if(zpp_inner.reverse_flag)push(obj)else unshift(obj)#if swc@:keep#end public function remove(obj:Arbiter):Bool{
        if(zpp_inner.immutable)throw "Error: "+"Arbiter"+"List is immutable";
        zpp_inner.modify_test();
        #if false zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=({
            var ret=false;
            {
                var cx_ite=zpp_inner.inner.begin();
                while(cx_ite!=null){
                    var x=cx_ite.elem();
                    {
                        if(x==obj.zpp_inner){
                            ret=true;
                            break;
                        }
                    };
                    cx_ite=cx_ite.next;
                }
            };
            ret;
        });
        if(ret){
            if(zpp_inner.subber!=null)zpp_inner.subber(obj);
            zpp_inner.inner.remove(obj.zpp_inner);
            zpp_inner.invalidate();
        }
        return ret;
    }
    #if swc@:keep#end public function empty(){
        #if false if(true)return length==0;
        else return zpp_inner.inner.empty();
        #else return length==0;
        #end
    }
    #if swc@:keep#end public function iterator(){
        #if false zpp_inner.valmod();
        #else zpp_vm();
        #end if(zpp_inner.iterators==null)zpp_inner.iterators=new ZNPList_ArbiterIterator();
        return ArbiterIterator.get(this);
    }
    #if swc@:keep#end public function copy(?deep:Bool=false){
        var ret=new ArbiterList();
        for(i in this)ret.push(deep?{
            throw "Error: "+"Arbiter"+" is not a copyable type";
        }
        :i);
        return ret;
    }
    public function new(){
        zpp_inner=new ZPP_ArbiterList();
        zpp_inner.outer=this;
    }
    @:keep public function toString(){
        var ret="[";
        var fst=true;
        for(i in this){
            if(!fst)ret+=",";
            ret+=i.toString();
            fst=false;
        }
        return ret+"]";
    }
}
#if swc@:keep#end class ArbiterIterator{
    public var zpp_inner:ArbiterList;
    public var zpp_i:Int;
    public var zpp_critical:Bool;
    public static var zpp_pool:ArbiterIterator;
    public var zpp_next:ArbiterIterator;
    public function new(){
        if(!ZPP_ArbiterList.internal)throw "Error: Cannot instantiate "+"Arbiter"+"Iterator derp!";
    }
    public static inline function get(list:ArbiterList){
        var ret=if(zpp_pool==null){
            ZPP_ArbiterList.internal=true;
            var ret=new ArbiterIterator();
            ZPP_ArbiterList.internal=false;
            ret;
        }
        else{
            var r=zpp_pool;
            zpp_pool=r.zpp_next;
            r;
        }
        ret.zpp_i=0;
        ret.zpp_inner=list;
        ret.zpp_critical=false;
        list.zpp_inner.iterators.add(ret);
        return ret;
    }
    #if swc@:keep#end public inline function hasNext(){
        #if true zpp_inner.zpp_inner.valmod();
        #else zpp_inner.zpp_vm();
        #end var length=zpp_inner.length;
        zpp_critical=true;
        if(zpp_i<length){
            return true;
        }
        else{
            zpp_next=zpp_pool;
            zpp_pool=this;
            zpp_inner.zpp_inner.iterators.remove(this);
            return false;
        }
    }
    #if swc@:keep#end public inline function next(){
        zpp_critical=false;
        return zpp_inner.at(zpp_i++);
    }
}
#if swc@:keep#end class Arbiter{
    public var zpp_inner:ZPP_Arbiter;
    public var id(flibget_id,never):Int;
    inline function flibget_id(){
        if(zpp_inner.cleared)throw "Error: Arbiter not currently in use";
        return zpp_inner.id;
    }
    public var type(flibget_type,never):ArbiterType;
    inline function flibget_type(){
        return[ArbiterType.COLLISION,ArbiterType.FLUID,ArbiterType.SENSOR][zpp_inner.type];
    }
    #if swc@:keep#end public inline function isCollisionArbiter(){
        return zpp_inner.type==ZPP_Arbiter.COL;
    }
    #if swc@:keep#end public inline function isFluidArbiter(){
        return zpp_inner.type==ZPP_Arbiter.FLUID;
    }
    #if swc@:keep#end public inline function isSensorArbiter(){
        return zpp_inner.type==ZPP_Arbiter.SENSOR;
    }
    public var collisionArbiter(flibget_collisionArbiter,never):CollisionArbiter;
    inline function flibget_collisionArbiter(){
        if(isCollisionArbiter())return zpp_inner.colarb.outer_zn;
        else return null;
    }
    public var fluidArbiter(flibget_fluidArbiter,never):FluidArbiter;
    inline function flibget_fluidArbiter(){
        if(isFluidArbiter())return zpp_inner.fluidarb.outer_zn;
        else return null;
    }
    public var shapeA(flibget_shapeA,never):Shape;
    public var shapeB(flibget_shapeB,never):Shape;
    inline function flibget_shapeA(){
        if(zpp_inner.cleared)throw "Error: Arbiter not currently in use";
        return(zpp_inner.ws1.id>zpp_inner.ws2.id)?zpp_inner.ws2.outer:zpp_inner.ws1.outer;
    }
    inline function flibget_shapeB(){
        if(zpp_inner.cleared)throw "Error: Arbiter not currently in use";
        return(zpp_inner.ws1.id>zpp_inner.ws2.id)?zpp_inner.ws1.outer:zpp_inner.ws2.outer;
    }
    public var bodyA(flibget_bodyA,never):Body;
    public var bodyB(flibget_bodyB,never):Body;
    inline function flibget_bodyA(){
        if(zpp_inner.cleared)throw "Error: Arbiter not currently in use";
        return(zpp_inner.ws1.id>zpp_inner.ws2.id)?zpp_inner.b2.outer:zpp_inner.b1.outer;
    }
    inline function flibget_bodyB(){
        if(zpp_inner.cleared)throw "Error: Arbiter not currently in use";
        return(zpp_inner.ws1.id>zpp_inner.ws2.id)?zpp_inner.b1.outer:zpp_inner.b2.outer;
    }
    public function new(){
        if(!ZPP_Arbiter.internal)throw "Error: Cannot instantiate Arbiter derp!";
    }
    @:keep public inline function toString(){
        var ret=if(isCollisionArbiter())"CollisionArbiter";
        else if(isFluidArbiter())"FluidArbiter";
        else "SensorArbiter";
        if(zpp_inner.cleared)return ret+"(object-pooled)";
        else return ret+"("+shapeA.toString()+"|"+shapeB.toString()+")"+(isCollisionArbiter()?"["+["SD","DD"][zpp_inner.colarb.kind]+"]":"");
    }
}
#if swc@:keep#end class CollisionArbiter extends Arbiter{
    public var contacts(flibget_contacts,never):ContactList;
    inline function flibget_contacts(){
        if(zpp_inner.cleared)throw "Error: Arbiter not currently in use";
        if(zpp_inner.colarb.wrap_contacts==null)zpp_inner.colarb.wrap_contacts=ZPP_ContactList.get(zpp_inner.colarb.contacts,true);
        return zpp_inner.colarb.wrap_contacts;
    }
    public function new(){
        if(!ZPP_Arbiter.internal)throw "Error: Cannot instantiate CollisionArbiter derp!";
        super();
    }
}
#if swc@:keep#end class FluidArbiter extends Arbiter{
    public var position(flibget_position,never):Vec2;
    inline function flibget_position(){
        if(zpp_inner.cleared)throw "Error: Arbiter not currently in use";
        if(zpp_inner.fluidarb.wrap_position==null)zpp_inner.fluidarb.getposition();
        return zpp_inner.fluidarb.wrap_position;
    }
    public var overlap(flibget_overlap,never):Float;
    inline function flibget_overlap(){
        if(zpp_inner.cleared)throw "Error: Arbiter not currently in use";
        return zpp_inner.fluidarb.overlap;
    }
    public function new(){
        if(!ZPP_Arbiter.internal)throw "Error: Cannot instantiate FluidArbiter derp!";
        super();
    }
}
